# -*- coding: utf-8 -*-
from gevent import monkey

monkey.patch_all(Event=True, subprocess=True)
from mongoengine import connect, Document, EmbeddedDocument, EmbeddedDocumentField \
    , EmailField, StringField, DateTimeField, IntField, FloatField, FileField \
    , BooleanField, ListField, ReferenceField, LongField, CASCADE, DENY, NULLIFY
from jobs import delScheduler
from packet import sendtodev
from cache import redis
from agent.alphabet import OK
import datetime
from functools import partial
import random
import os
import setting
import logging

logger = logging.getLogger('web')

if setting.db['username'] and setting.db['password']:
    connect(
        db=setting.db['database'],
        host='mongodb://%s:%s@%s' % (setting.db['username'], setting.db['password'], setting.db['host'])
    )
else:
    connect(db=setting.db['database'])

randint = partial(random.randint, a=0, b=999999999)
# 生成安全区域随机id

chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'


def randchr24():
    c = ''
    for i in xrange(24):
        c += random.choice(chars)
    return c


class Admin(Document):
    username = StringField(max_length=20, unique=True, required=True)
    password = StringField(max_length=40, required=True)
    type = StringField(max_length=10, required=True)

    def delete(self):
        redis.delete('Admin:' + str(self.id))
        super(Admin, self).delete()


class Saferegion(EmbeddedDocument):
    id = LongField(default=randint)
    name = StringField(max_length=30, required=True)
    address = StringField(required=True)
    lon = FloatField(required=True)
    lat = FloatField(required=True)
    radius = IntField(required=True)


class JobBox(EmbeddedDocument):
    instruct = StringField(max_length=10, required=True)
    params = StringField(required=True)
    # 与指令相同,用","分隔参数
    time = DateTimeField(default=datetime.datetime.now)


class Silent(EmbeddedDocument):
    num = IntField(required=True)
    # 1,2,3
    weekday = StringField(max_length=20, required=True)
    # '1,2,3,4,5'
    starttime = DateTimeField(required=True)
    endtime = DateTimeField(required=True)
    status = BooleanField(default=False)


class DeviceAudio(EmbeddedDocument):
    content = FileField(collection_name='device.audio')


class Device(Document):
    imei = StringField(max_length=15, min_length=15, primary_key=True)
    portrait = StringField(default='')
    version = StringField()
    imgkey = StringField(default=randchr24)
    # 图片文件名
    name = StringField(max_length=50, default='')
    age = IntField(default=0)
    height = IntField(default=0)
    weight = IntField(default=0)
    sex = StringField(max_length=10, default='')
    steplen = IntField(default=0)
    voltage = FloatField(default=0)
    birthday = DateTimeField()
    phone = StringField(max_length=20, default='')
    whitelist = ListField(StringField(max_length=20))
    family = ListField(LongField())
    maketime = DateTimeField(default=datetime.datetime.now)
    lasttime = DateTimeField(default=datetime.datetime.now)
    altertime = DateTimeField(default=datetime.datetime.now)
    localintervalminute = IntField(default=10)
    # 定位间隔,默认十分钟
    lastRegionId = IntField(default=0)
    # 最后一次进入的安全区域的id
    sos = BooleanField(default=False)
    # 当前是否为sos状态
    silentoption = BooleanField(default=False)
    # 当前是否静音
    modified = IntField(default=1)
    timezone = IntField(default=8)

    audio = ListField(EmbeddedDocumentField(DeviceAudio))
    saferegion = ListField(EmbeddedDocumentField(Saferegion))
    jobbox = ListField(EmbeddedDocumentField(JobBox))
    silent = ListField(EmbeddedDocumentField(Silent))

    meta = {
        'cascade': True
    }

    def updatePortrait(self, portrait):
        self.portrait = portrait
        imgkey = randchr24()
        self.imgkey = imgkey
        self.save()
        now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        redis.hset('Device:%s' % self.imei, 'altertime', now)
        try:
            portrait = portrait.decode('base64')
        except:
            portrait = ''
        redis.hset('Device:%s' % self.imei, 'portrait', portrait)

    def _freshWhiteList(self, addphone=None, delphone=None):
        if self.whitelist:
            k = 0
            try:
                while True:
                    if addphone:
                        self.whitelist.append(addphone)
                        k = 1
                    elif delphone:
                        self.whitelist.remove(delphone)
                        k = 1
                    else:
                        self.whitelist.remove('')
                        k = 1
            except ValueError:
                pass
            padding = 10 - len(self.whitelist)
            whiteString = str(';'.join(self.whitelist) + ';' * padding)
            # sos = []
            # if not familyphone:
            #     familyphone = []
            #     for u in User.objects(id__in=self.family):
            #         familyphone.append(u.phone)
            # padding = 11 - len(familyphone[:setting.family['sosnum']])
            # sosString = str(';'.join(familyphone[:setting.family['sosnum']])+';'*padding)
            username = {}
            for u in User.objects(id__in=self.family):
                username[u.phone] = getuserdevRelate(u.relateInfo).encode('utf8')
            namelist = []
            overname = {}
            for phone in self.whitelist:
                try:
                    name = username[phone]
                except KeyError:
                    name = '成员'
                if name not in namelist:
                    namelist.append(name)
                else:
                    if name not in overname:
                        namelist.append(name + '1')
                        overname[name] = 2
                    else:
                        namelist.append(name + str(overname[name]))
                        overname[name] += 1
            nameString = str(';'.join(namelist) + ';' * padding)
            s19 = sendtodev(self.imei, 'S19', [whiteString, nameString])
            # 速拨号码
            s20 = sendtodev(self.imei, 'S20', [whiteString, nameString])
            # 白名单
            if k or s19 != OK or s20 != OK:
                self.modified = 1
                self.save()
            return s19 == s20 == OK

    def delWhiteList(self, phone):
        if self.whitelist:
            familyphone = []
            for u in User.objects(id__in=self.family):
                familyphone.append(u.phone)
            if phone in familyphone:
                return None
            try:
                self.whitelist.remove(phone)
                self.save()
                return self._freshWhiteList()
            except ValueError:
                pass

    def addWhiteList(self, phone):
        if phone not in self.whitelist and len(self.whitelist) < 10:
            familyphone = []
            whitelist = self.whitelist[:]
            adminphone = False
            for user in User.objects(id__in=self.family):
                if user.admin == user.id:
                    adminphone = user.phone
                else:
                    if user.phone:
                        familyphone.append(user.phone)
                try:
                    whitelist.remove(user.phone)
                except ValueError:
                    pass
            if adminphone:
                familyphone.insert(0, adminphone)
            if phone and (phone not in familyphone) and (phone not in whitelist):
                whitelist.append(phone)
            whitephone = familyphone[:]
            whitephone.extend(whitelist)
            self.whitelist = whitephone
            self.save()
        return self._freshWhiteList()

    def delete(self):
        try:
            for audio in self.audio:
                audio.content.delete()
            for userdev in UserDev.objects(dev=self.imei):
                if userdev.type == 'common' or (
                                userdev.type == 'admin' and UserDev.objects(user=userdev.user).count() == 1):
                    userdev.user.refresh()
            for s in self.silent:
                delScheduler('%s_%s_on' % (self.imei, s.num))
                delScheduler('%s_%s_off' % (self.imei, s.num))
            sendtodev(self.imei, 'S19', [';' * 10, ';' * 10])
            sendtodev(self.imei, 'S20', [';' * 10, ';' * 10])
            sendtodev(self.imei, 'S45', [setting.default['age'], setting.default['height'], setting.default['steplen'],
                                         setting.default['weight'], setting.default['sex'], '|宝贝|'])
            Message.objects(sender=str(self.imei), type='baby').delete()
        except:
            logger.critical('delete Device failed', exc_info=True)
        super(Device, self).delete()


class Authresult(EmbeddedDocument):
    dev = ReferenceField(Device)
    result = StringField(max_length=20, required=True)
    time = DateTimeField(default=datetime.datetime.now)


class User(Document):
    id = LongField(primary_key=True)
    phone = StringField(max_length=20, default='')
    email = EmailField(max_length=60)
    password = StringField(max_length=50, default='')
    token = StringField(default='')
    relateInfo = StringField(default='')
    # 用户绑定宝贝关系,用于显示用户头像
    admin = LongField(default=0)
    maketime = DateTimeField(default=datetime.datetime.now)
    lasttime = DateTimeField(default=datetime.datetime.now)
    model = StringField(max_length=25, default='')
    authresult = ListField(EmbeddedDocumentField(Authresult))
    mesageReadTime = DateTimeField(default=datetime.datetime.min)
    # 家庭圈消息列表已读指针

    meta = {
        'cascade': True,
        'indexes': ['phone']
    }

    def __repr__(self):
        return 'User %i' % self.id

    def refresh(self):
        self.admin = 0
        self.relateInfo = ''
        self.save()

    @property
    def portrait(self):
        # 根据用户绑定的关系返回用户具有的头像
        try:
            return relatePortrait[self.relateInfo]
        except KeyError:
            return ''

    def delete(self):
        try:
            if self.id == self.admin:
                for userdev in UserDev.objects(user=self.id):
                    dev = userdev.dev
                    userdev.delete()
                    dev.delete()
            else:
                try:
                    userdev = UserDev.objects(user=self.id).get()
                    userdev.delete()
                    # 用 objects 的delete方法不会调用到类的delete方法
                except UserDev.DoesNotExist:
                    pass
            Message.objects(sender=str(self.id), type='user').delete()
        except:
            logger.critical('delete User failed', exc_info=True)
        super(User, self).delete()


class UserDevRelate(Document):
    value = StringField(max_length=20, primary_key=True)
    relate = StringField(max_length=20, required=True)

    meta = {
        'collection': 'userdevRelate',
        'indexes': ['relate']
    }

    def delete(self):
        try:
            relatePortrait.pop(self.value)
        except KeyError:
            pass
        super(UserDevRelate, self).delete()


class UserDev(Document):
    user = ReferenceField(User, reverse_delete_rule=CASCADE)
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    relate = ReferenceField(UserDevRelate, reverse_delete_rule=DENY)
    type = StringField(max_length=10, required=True)
    # 'admin' 管理员 'common' 普通成员
    time = DateTimeField(default=datetime.datetime.now)

    meta = {
        'collection': 'userdev',
        'indexes': ['user', 'dev']
    }

    def delete(self, admin=None):
        try:
            if self.type == 'admin':
                for ud in UserDev.objects(dev=self.dev):
                    if ud.type != 'admin':
                        ud.delete(self.user)
                babylist = []
                for ud in UserDev.objects(user=self.user):
                    babylist.append(ud.dev)
                if len(babylist) > 1:
                    userlist = []
                    for ud in UserDev.objects(dev__in=babylist):
                        if ud.user.id not in userlist:
                            userlist.append(ud.user.id)
                    if len(userlist) > 1:
                        for baby in babylist:
                            if baby != self.dev:
                                baby.family = userlist
                                baby.save()
                self.dev.delete()
            else:
                user = self.user
                if not admin:
                    admin = UserDev.objects(dev=self.dev, type='admin').get().user
                for ud in UserDev.objects(user=admin):
                    try:
                        ud.dev.family.remove(user.id)
                    except KeyError:
                        pass
                    ud.dev._freshWhiteList(delphone=user.phone)
                user.refresh()
        except:
            logger.critical('delete UserDev failed', exc_info=True)
        super(UserDev, self).delete()


class DevData(Document):
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    step = IntField(required=True)
    distance = FloatField(required=True)
    calories = FloatField(required=True)
    date = DateTimeField(default=datetime.date.today, unique_with='dev')

    meta = {'collection': 'devdata'}


class Disadmin(Document):
    user = ReferenceField(User, reverse_delete_rule=CASCADE)
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    time = DateTimeField(default=datetime.datetime.now)

    meta = {
        'indexes': ['user', 'dev']
    }


class Disauth(Document):
    admin = ReferenceField(User, reverse_delete_rule=CASCADE)
    adminphone = StringField(max_length=20, required=True)
    user = ReferenceField(User, reverse_delete_rule=CASCADE)
    userphone = StringField(max_length=20, required=True)
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    relate = ReferenceField(UserDevRelate, reverse_delete_rule=DENY)
    time = DateTimeField(default=datetime.datetime.now)

    meta = {
        'indexes': ['admin', 'user']
    }


class StepAward(Document):
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    target = LongField(required=True)
    starttime = DateTimeField(default=datetime.datetime.now)
    endtime = DateTimeField(default=datetime.datetime.min)
    done = BooleanField(default=False)

    meta = {
        'collection': 'stepaward',
        'indexes': ['dev']
    }


class StarAward(Document):
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    target = LongField(required=True)
    star = LongField(default=0)
    award = StringField(max_length=30, default='')
    level1 = StringField(max_length=30, default='')
    level2 = StringField(max_length=30, default='')
    level3 = StringField(max_length=30, default='')
    starttime = DateTimeField(default=datetime.datetime.now)
    endtime = DateTimeField(default=datetime.datetime.min)
    done = BooleanField(default=False)

    meta = {
        'collection': 'staraward',
        'indexes': ['dev']
    }


class Maprecord(Document):
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    address = StringField(required=True)
    province = StringField(max_length=10, required=True)
    city = StringField(max_length=20, required=True)
    road = StringField(max_length=50, required=True)
    poi = StringField(required=True)
    citycode = StringField(max_length=10, required=True)
    adcode = StringField(max_length=10, required=True)
    lon = FloatField(required=True)
    lat = FloatField(required=True)
    lbs = StringField(required=True)
    radius = IntField(required=True)
    useGPS = BooleanField(default=False)
    # 是否使用GPS定位,S29指令
    rawLon = FloatField(default=0.0)
    rawLat = FloatField(default=0.0)
    type = StringField(default='automate')
    # 'automate' 自动定位,'manual' 手动定位点击
    time = DateTimeField(default=datetime.datetime.now)

    meta = {'indexes': ['dev', 'time']}


class MessageCenter(Document):
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    type = StringField(required=True)
    value = StringField()
    time = DateTimeField(default=datetime.datetime.now)

    meta = {
        'collection': 'messagecenter',
        'indexes': ['dev']
    }


class Apprecord(Document):
    user = ReferenceField(User, reverse_delete_rule=CASCADE)
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    address = StringField(required=True)
    lon = FloatField(required=True)
    lat = FloatField(required=True)
    time = DateTimeField(default=datetime.datetime.now)

    meta = {'indexes': ['user', 'dev']}


class Telephone(Document):
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    timing = StringField(required=True)
    time = DateTimeField(default=datetime.datetime.now)

    meta = {'indexes': ['dev']}


class Feedback(Document):
    user = ReferenceField(User, reverse_delete_rule=NULLIFY)
    content = StringField(required=True)
    time = DateTimeField(default=datetime.datetime.now)


class Message(Document):
    sender = StringField(max_length=20, required=True)
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    content = StringField(required=True)
    audioid = LongField(required=True)
    audiolen = LongField(required=True)
    type = StringField(max_length=10, required=True)
    # 'user' 为用户发送, 'baby' 为宝贝发送
    time = DateTimeField(default=datetime.datetime.now)

    meta = {
        'indexes': ['sender', 'dev', 'audioid', 'time']
    }


class SOS(Document):
    dev = ReferenceField(Device, reverse_delete_rule=CASCADE)
    address = StringField(required=True)
    time = DateTimeField(default=datetime.datetime.now)
    done = BooleanField(default=False)
    lon = FloatField()
    lat = FloatField()

    meta = {
        'collection': 'sos',
        'indexes': ['dev', 'time']
    }


class Loger(Document):
    model = StringField(required=True)
    # server,web
    ident = StringField(required=True)
    # imei,userid
    event = StringField(required=True)
    value = StringField(required=True)
    time = DateTimeField(default=datetime.datetime.now)

    meta = {
        'indexes': ['ident', 'time']
    }

    def __repr__(self):
        return "%s %s %s %s %s" % (self.time, self.model, self.ident, self.event, self.value)


# Admin.objects().get_or_create(username='admin',password='e10adc3949ba59abbe56e057f20f883e',type='admin')

UserDevRelate.objects(value='father').update(set__relate='爸爸', upsert=True)
UserDevRelate.objects(value='mother').update(set__relate='妈妈', upsert=True)
UserDevRelate.objects(value='uncle').update(set__relate='叔叔', upsert=True)
UserDevRelate.objects(value='aunt').update(set__relate='阿姨', upsert=True)
UserDevRelate.objects(value='grandpa').update(set__relate='爷爷', upsert=True)
UserDevRelate.objects(value='grandma').update(set__relate='奶奶', upsert=True)
# 初始化数据库

userdevRelate = {}
for i in UserDevRelate.objects():
    userdevRelate[i.value] = i.relate
userdevRelateList = []
for i in userdevRelate:
    userdevRelateList.append({'value': i, 'relate': userdevRelate[i]})

relatewrite = 0
relateread = 0


# 写标识与读标识,用于获取关系列表时从内存中获取
# 有更新时再刷新对象内容,返回关系列表

def updateUserDevRelate():
    userdevRelate.clear()
    for i in UserDevRelate.objects():
        userdevRelate[i.value] = i.relate
    userdevRelateList[:] = []
    # 列表的清空操作,这样才能不改变列表的引用地址
    for i in userdevRelate:
        userdevRelateList.append({'value': i, 'relate': userdevRelate[i]})
    relateread = relatewrite = 0


def getuserdevRelate(relate=None):
    if relate is None:
        return userdevRelate.copy()
    if relate in userdevRelate:
        return userdevRelate[relate]
    else:
        return ''


relatePortrait = {}


def updateRelatePortrait():
    for filename in os.listdir('./admin/static/relate'):
        key = filename.split('.')[0]
        try:
            value = open('./admin/static/relate/' + filename, 'rb').read().encode('base64')
        except IOError:
            value = ''
        relatePortrait.update({key: value})


if os.getcwd()[-3:] == 'web':
    updateRelatePortrait()
